package com.lj.tools.util;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

public class RSAUtil {

    //加密算法
    private final static String ALGORITHM_RAS = "RAS";

    /**
     * 直接生成公钥、私钥
     * */
    public static KeyPair getRSAKeyObject(int modulus) throws NoSuchAlgorithmException {
        //List<Key> keyList = new ArrayList<>(2);
        //创建RAS密钥生成器
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM_RAS);
        //设置密钥的大小，RSA算法的模长=最大加密数据的大小
        keyPairGenerator.initialize(modulus);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        //keyPair.getPublic();
        //keyPair.getPrivate();
        return keyPair;
    }

    /**
     * 生成字符串
     * */
    public static List<String> getRSAKeyString(int modulus) throws NoSuchAlgorithmException {
        List<String> keyList = new ArrayList<>(2);
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM_RAS);
        keyPairGenerator.initialize(modulus);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        keyList.add(Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()));
        keyList.add(Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()));
        return keyList;
    }

    /**
     * JAVA RSAPublicKeySpec  X509EncodedKeySpec支持生成
     * */
    public static RSAPublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RAS);
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        return (RSAPublicKey) keyFactory.generatePublic(spec);
    }

    public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RAS);
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        //X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        return (RSAPrivateKey) keyFactory.generatePrivate(spec);
    }


    public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM_RAS);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int modulusSize = publicKey.getModulus().bitLength()/8;
        int maxSingleSize = modulusSize = 11;
        byte[][] dataArray = splitArray(data.getBytes(), maxSingleSize);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        for (byte[] s : dataArray){

        }
        return Base64.getEncoder().encodeToString(out.toByteArray());
    }

    public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws  Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM_RAS);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        int modulusSize = privateKey.getModulus().bitLength()/8;
        byte[] dataBytes = data.getBytes();
        byte[] decodeData = Base64.getDecoder().decode(dataBytes);
        byte[][] splitArrays = splitArray(decodeData, modulusSize);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        for (byte[] arr: splitArrays){
            out.write(cipher.doFinal(arr));
        }
        return new String(out.toByteArray());
    }

    private static byte[][] splitArray(byte[] data, int len){
        int dataLen = data.length;
        if (dataLen <= len){
            return new byte[][]{data};
        }
        byte[][] result = new byte[(dataLen-1)/len+1][];
        int resultLen = result.length;
        for (int i = 0; i < resultLen; i++){
            if ( i == resultLen - 1){
                int slen = dataLen - len*i;
                byte[] single = new byte[slen];
                System.arraycopy(data, len*i, single, 0, slen);
                result[i] = single;
                break;
            }
            byte[] single = new byte[len];
            System.arraycopy(data, len*i, single, 0, len);
            result[i] = single;
        }
        return result;
    }

}
